package com.bluetrum.abmate.eq;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;

import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import com.bluetrum.abmate.R;
import com.bluetrum.abmate.adapter.EqAdapter;
import com.bluetrum.abmate.adapter.GridSpaceItemDecoration;
import com.bluetrum.abmate.cmd.SimpleRequestCallback2;
import com.bluetrum.abmate.databinding.ActivityEqBinding;
import com.bluetrum.abmate.dialog.DialogFragmentEqName;
import com.bluetrum.abmate.utils.Utils;
import com.bluetrum.abmate.viewmodels.SharedViewModel;
import com.bluetrum.devicemanager.cmd.Command;
import com.bluetrum.devicemanager.cmd.Request;
import com.bluetrum.devicemanager.cmd.request.DeviceInfoRequest;
import com.bluetrum.devicemanager.cmd.request.EqRequest;
import com.bluetrum.devicemanager.models.RemoteEqSetting;
import com.bluetrum.utils.HexUtils;
import com.bluetrum.view.equalizerview.EqualizerView;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

import javax.inject.Inject;

import dagger.hilt.android.AndroidEntryPoint;
import timber.log.Timber;

@AndroidEntryPoint
public class EqActivity extends AppCompatActivity
        implements EqAdapter.OnItemClickListener, DialogFragmentEqName.DialogFragmentEqNameListener {

    private static final String DIALOG_FRAGMENT_EQ_NAME = "DIALOG_FRAGMENT_EQ_NAME";

    private SharedViewModel mViewModel;

    private ActivityEqBinding binding;

    private EqAdapter mAdapter;
    private StaggeredGridLayoutManager mLayoutManager;
    private GridSpaceItemDecoration mItemDecoration;

    private static final int RECYCLER_VIEW_SPAN_COUNT = 3;

    private static final int ROW_SPACING = 4;
    private static final int COLUMN_SPACING = 4;

    private List<EqSetting> eqSettings;

    @Inject
    DeviceEqApi deviceEqApi; // TODO: 放ViewModel

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mViewModel = new ViewModelProvider(this).get(SharedViewModel.class);
        binding = ActivityEqBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        setupEqualizerView();
        setupRecyclerView();

        deviceEqApi.getEqSettingsLiveData().observe(this, this::customEqSettingsChanged); // 数据库中的自定义EQ
        mViewModel.getDeviceEqSetting().observe(this, this::eqSettingChanged); // 设备当前EQ设置

        // 获取最新EQ配置
        mViewModel.sendRequest(new DeviceInfoRequest(Command.INFO_EQ_SETTING));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_eq, menu);
        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MenuItem editMenuItem = menu.findItem(R.id.edit);
        if (editMenuItem != null) {
            editMenuItem.setVisible(!mAdapter.isSelectMode());
        }
        MenuItem cancelMenuItem = menu.findItem(R.id.cancel);
        if (cancelMenuItem != null) {
            cancelMenuItem.setVisible(mAdapter.isSelectMode());
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        int itemId = item.getItemId();
        if (itemId == R.id.edit) {
            mAdapter.setSelectMode(true);
            invalidateOptionsMenu();
            // 隐藏EQ
            binding.equalizerView.setVisibility(View.GONE);
            return true;
        }
        else if (itemId == R.id.cancel) {
            mAdapter.setSelectMode(false);
            invalidateOptionsMenu();
            // 显示EQ
            binding.equalizerView.setVisibility(View.VISIBLE);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void setupEqualizerView() {
        ArrayList<Integer> bands = new ArrayList<Integer>() {{
            add(8);
            add(31);
            add(62);
            add(125);
            add(250);
            add(500);
            add(1000);
            add(2000);
            add(4000);
            add(16000);
        }};

        binding.equalizerView.setBandListener(new EqualizerView.EventListener() {
            @Override
            public void onBandLevelChanged(int bandId, int progress, boolean fromUser) {
                if (fromUser) {
                    byte[] levels = getGains();

                    int level = binding.equalizerView.getBandLevel(bandId) - EqSetting.EQUALIZER_MAX_GAIN;
                    Timber.v("bandId: %s, level: %s, levels: %s", bandId, level, Arrays.toString(levels));

                    mAdapter.setCurrentEqSetting(levels);

                    Request request = EqRequest.CustomEqRequest((byte) 0, levels); // 第一个参数目前没用到
                    sendRequestAndWaitSuccess(request, null);
                }
            }

            @Override
            public void onStart(int bandId) {
                // empty
            }

            @Override
            public void onStop(int bandId) {
                // empty
            }
        });

        binding.equalizerView.setMotionEventSplittingEnabled(false); // 禁止多点
        binding.equalizerView.setOnlyUpdateWhenTouchUp(true);
        binding.equalizerView.setBands(bands);
        binding.equalizerView.setMax(EqSetting.EQUALIZER_MAX_GAIN * 2); // ±
        binding.equalizerView.draw();
    }

    private void setupRecyclerView() {
        int spanCount = RECYCLER_VIEW_SPAN_COUNT;
        mLayoutManager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL);
        binding.recyclerView.setLayoutManager(mLayoutManager);

        mItemDecoration = new GridSpaceItemDecoration(spanCount, ROW_SPACING, COLUMN_SPACING);
        binding.recyclerView.addItemDecoration(mItemDecoration);

        mAdapter = new EqAdapter(this, null, spanCount);
        mAdapter.setOnItemClickListener(this);
        binding.recyclerView.setAdapter(mAdapter);
    }

    private void customEqSettingsChanged(List<EqSetting> newCustomEqSettings) {
        List<EqSetting> allEqSettings = new ArrayList<EqSetting>() {{
            addAll(PresetEqSetting.allPresetEqSettings());
            addAll(newCustomEqSettings);
        }};
        this.eqSettings = allEqSettings;
        mAdapter.setEqSettings(allEqSettings);
    }

    private void eqSettingChanged(RemoteEqSetting eqSetting) {
        // 检查有效性
        if (eqSetting != null) {
            Timber.v("EQ Hex: %s", HexUtils.bytesToHex(eqSetting.getGains()));
            showBandLevels(eqSetting.getGains());
            mAdapter.setCurrentEqSetting(eqSetting.getGains());
        }
    }

    private byte[] getGains() {
        Map<String, Integer> levelsMap = binding.equalizerView.getBandLevels();
        ByteBuffer bb = ByteBuffer.allocate(levelsMap.size());
        // 按照Key排序，并按照格式{gain}放到ByteArray
        levelsMap.entrySet().stream().sorted(
                Comparator.comparingInt(entry -> Integer.parseInt(entry.getKey()))
        ).forEachOrdered(entry -> bb.put((byte) (entry.getValue().byteValue() - EqSetting.EQUALIZER_MAX_GAIN)));
        return bb.array();
    }

    private void showBandLevels(byte[] gains) {
        Map<String, Integer> map = IntStream.range(0, gains.length).boxed()
                .collect(HashMap::new, (m, v) -> {
                    m.put(Integer.toString(v), gains[v] + EqSetting.EQUALIZER_MAX_GAIN);
                }, (m1, m2) -> {

                });
        binding.equalizerView.setBandLevels(map);
    }

    /* EqAdapter.OnItemClickListener */

    @Override
    public void onItemWillBeDeleted(EqSetting eqSetting) {
        String alertTitle = getResources().getString(R.string.eq_alert_will_be_deleted, eqSetting.getName());

        final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this)
                .setTitle(alertTitle)
                .setPositiveButton(R.string.ok, (dialog, which) -> deviceEqApi.deleteEqSetting(eqSetting))
                .setNegativeButton(R.string.cancel, null);
        alertDialogBuilder.show();
    }

    @Override
    public void OnItemClick(EqSetting eqSetting) {
        if (eqSetting == PresetEqSetting.EQ_SETTING_ADD) {
            DialogFragmentEqName dialogFragmentEqName
                    = DialogFragmentEqName.newInstance("");
            dialogFragmentEqName.show(getSupportFragmentManager(), DIALOG_FRAGMENT_EQ_NAME);
        } else {
            final byte[] gains = eqSetting.getGains();
            showBandLevels(gains);
            mAdapter.setCurrentEqSetting(gains);

            // Send request
            int index = eqSettings.indexOf(eqSetting);
            Request request = EqRequest.PresetEqRequest((byte) index, gains); // 第一个参数，固件只关心预设
            sendRequestAndWaitSuccess(request, null);
        }
    }

    @Override
    public void onItemLongClick(EqSetting eqSetting) {
        if (eqSetting.isCustom()) {
            mAdapter.setSelectMode(true);
            invalidateOptionsMenu();
            // 隐藏EQ
            binding.equalizerView.setVisibility(View.GONE);
        }
    }

    /* DialogFragmentEqName.DialogFragmentEqNameListener */

    @Override
    public boolean onEqNameUpdated(@NonNull String eqName) {
        byte[] levels = getGains();
        deviceEqApi.insertEqSetting(new CustomEqSetting(eqName, levels));
        return true;
    }

    /* Request */

    private void startWaitingResponse() {
//        binding.progressIndicator.setVisibility(View.VISIBLE);
//        binding.scrollView.setEnabled(false); // todo: 没用
    }

    private void stopWaitingResponse() {
//        binding.progressIndicator.setVisibility(View.GONE);
//        binding.scrollView.setEnabled(true); // todo: 没用
    }

    /*
    统一处理返回值Boolean的Request，并提示可能的失败和超时
     */
    private void sendRequestAndWaitSuccess(@NonNull final Request request,
                                           @Nullable SimpleRequestCallback2 callback2) {
        // todo: 加上发送等待画面
        startWaitingResponse();

        mViewModel.sendRequest(request, new SimpleRequestCallback2() {
            @Override
            public void onComplete(boolean result) {
                Timber.d("sendRequest result = %s", result);

                if (result) {
                    Utils.showToast(EqActivity.this, R.string.request_succeeded);
                } else {
                    Utils.showToast(EqActivity.this, R.string.request_failed);
                }

                // todo: 解除发送等待
                stopWaitingResponse();

                if (callback2 != null) {
                    callback2.onComplete(result);
                }
            }

            @Override
            public void onTimeout() {
                Timber.w("Request timeout");
                Utils.showToast(EqActivity.this, R.string.request_timeout);

                // todo: 解除发送等待
                stopWaitingResponse();

                if (callback2 != null) {
                    callback2.onTimeout();
                }
            }
        });
    }

}